# -*- coding: utf-8 -*-
"""
    xfree.core.deprecation
    ~~~~~~~~~~~~~~~~~~~
    文件描述：这个模块用于在 xfree 中处理弃用警告。
    作者：xfree
    邮箱：cy_83k@qq.com
"""

import inspect
import warnings
from abc import ABC, abstractproperty
from functools import wraps

from flask_babelplus import gettext as _
# `gettext` 被导入用于实现国际化和文本的本地化。

# `XFreeWarning` 类是 xfree 自身需要发出的任何警告的基类。
# 它提供了一种方便的方式来过滤特定于 xfree 的警告。
class XFreeWarning(Warning):
    """
    xfree 自身发出的任何警告的基类，用于方便地进行过滤。
    """
    pass

# `XFreeDeprecation` 是 `DeprecationWarning` 和 `XFreeWarning` 的子类。
# 它是 xfree 中弃用警告的基类。
# 子类必须提供一个 `version` 属性，表示弃用何时变为移除。
class XFreeDeprecation(DeprecationWarning, XFreeWarning, ABC):
    """
    xfree 中弃用警告的基类，子类必须提供一个 `version` 属性，
    该属性表示弃用何时变为移除。

    例如：

    class RemovedInPluginv3(XFreeDeprecation):
        version = (3, 0, 0)
    """

    # 这是一个抽象属性，子类必须实现它以提供弃用变为移除的版本。
    version = abstractproperty(lambda self: None)

# `RemovedInXFree3` 是一个特定的弃用警告类，用于在 xfree 3 中被移除的功能。
class RemovedInXFree3(XFreeDeprecation):
    """
    用于在 xfree 3 中被移除的功能的警告。
    """
    version = (3, 0, 0)

# `deprecated` 装饰器函数用于标记一个函数或方法为已弃用。
def deprecated(message="", category=RemovedInXFree3):
    """
    将一个函数或方法标记为已弃用。

    参数：
    - `message`：可选的消息，用于在弃用警告中显示额外的说明。
    - `category`：警告类别，默认为 `RemovedInXFree3`。必须是 `XFreeDeprecation` 的子类。

    """

    def deprecation_decorator(f):
        # 检查传入的 `category` 是否是 `XFreeDeprecation` 的子类。
        if not issubclass(category, XFreeDeprecation):
            raise ValueError(
                "预期 `category` 是 `XFreeDeprecation` 的子类，得到了 {}".format(  # noqa
                    str(category)
                )
            )

        # 根据传入的警告类别获取版本信息，并将其转换为字符串格式。
        version = ".".join([str(x) for x in category.version])

        # 构建警告消息，包括函数名和弃用版本信息。
        warning = _(
            "%(name)s 已弃用，将在版本 %(version)s 中被移除。",  # noqa
            name=f.__name__,
            version=version,
        )
        # 如果提供了额外的消息，则将其添加到警告消息中。
        if message:
            warning = "{} {}".format(warning, message)

        # 获取被装饰函数的文档字符串。
        docstring = f.__doc__

        # 如果函数有文档字符串，则将警告消息添加到文档字符串末尾；
        # 如果没有文档字符串，则直接将警告消息作为文档字符串。
        if docstring:
            docstring = "\n".join([docstring, warning])
        else:
            docstring = warning

        # 设置被装饰函数的文档字符串为更新后的内容。
        f.__doc__ = docstring

        # 使用 `wraps` 装饰器保留被装饰函数的元数据，并在新的函数中发出警告。
        @wraps(f)
        def wrapper(*a, **k):
            # 获取当前帧的上一帧。
            frame = inspect.currentframe().f_back
            # 发出明确的警告，包括警告消息、警告类别、文件名和行号。
            warnings.warn_explicit(
                warning,
                category=category,
                filename=inspect.getfile(frame.f_code),
                lineno=frame.f_lineno,
            )
            # 执行被装饰的函数。
            return f(*a, **k)

        return wrapper

    return deprecation_decorator